﻿/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Jurgen Van Gael
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

using System;
using CalcSharp.Core.Properties;

namespace dnAnalytics.Statistics.Distributions
{
    /// <summary>
    /// A class which represents a random permutation.
    /// </summary>
    /// <remarks>The distribution will use the <see cref="System.Random"/> by default. 
    /// Users can set the random number generator by using the <see cref="RandomNumberGenerator"/> property.
    /// 
    /// The statistics classes will check all the incoming parameters whether they are in the allowed
    /// range. This might involve heavy computation. Optionally, by setting Control.CheckDistributionParameters
    /// to false, all parameter checks can be turned off.</remarks>
    public class Permutation
    {
        /// <summary>
        /// The upper bound of the permutation.
        /// </summary>
        private readonly int mK;

        /// <summary>
        /// Construct a new permutation with the range 0..k-1.
        /// </summary>
        /// <param name="k">Upper bound of the permutation range.</param>
        public Permutation(int k)
        {
            if (Control.CheckDistributionParameters)
            {
                CheckParameters(k);
            }
            
            mK = k;
            RandomNumberGenerator = new System.Random();
        }

        /// <summary>
        /// Gets or sets the random number generator.
        /// </summary>
        /// <value>The random number generator used to generate a random sample.</value>
        public System.Random RandomNumberGenerator { get; set; }

        /// <summary>
        /// Check the parameters of the distribution.
        /// </summary>
        /// <param name="k">The size of the permutation.</param>
        /// <exception cref="ArgumentOutOfRangeException">If k is non-positive.</exception>
        public static void CheckParameters(int k)
        {
            if (k <= 0)
            {
                throw new ArgumentOutOfRangeException("k", Resources.NotPositive);
            }
        }

        /// <summary>
        /// Samples a random permutation of the numbers between 0 and k-1 inclusive.
        /// </summary>
        public int[] Sample()
        {
            return Sample(RandomNumberGenerator, mK);
        }

        /// <summary>
        /// Samples a random permutation of the numbers between 0 and k-1 inclusive.
        /// </summary>
        /// <param name="rnd">The random number generator to use.</param>
        /// <param name="k">The end of the range of the permutation (exclusive).</param>
        public static int[] Sample(System.Random rnd, int k)
        {
            if (Control.CheckDistributionParameters)
            {
                CheckParameters(k);
            }

            int[] arr = new int[k];
            for (int i = 0; i < k; i++)
            {
                arr[i] = i;
            }

            for (int i = 0; i < k; i++)
            {
                int j = rnd.Next()%(i + 1);
                if (j != i)
                {
                    int t = arr[j];
                    arr[j] = arr[i];
                    arr[i] = t;
                }
            }

            return arr;
        }
    }
}